#include "Model/DailyCalendarRule.hpp"
#include <cassert>

namespace Model
{

	void DailyCalendarRule::calculatePeriodSet( const Calendar::Time& start, const Calendar::Time& end, Calendar::PeriodSet& periods )
	{
		assert( start <= end);
		// for each day, take the daily ranges and merge them into the period set.
		// It takes care of the cases when a range from one day finishes just when the range from the next day starts.
		Calendar::Date curDate = start.date(),
			endDate = end.date();
		while( curDate <= endDate)
		{
			this->mergeDailyPeriodsForOneDay( curDate, periods);
			curDate += boost::gregorian::days( 1);
		}
		periods.intersect( start, end);
	}

// ---------------------------------------------------------------------------------------------------------------------

	void DailyCalendarRule::mergeDailyPeriod( ShortTime start, ShortTime end )
	{
		assert( 24 >= DailyCalendarRule::calculateHours( start));
		assert( 24 >= DailyCalendarRule::calculateHours( end));
		assert( 60 > DailyCalendarRule::calculateMinutes( start));
		assert( 60 > DailyCalendarRule::calculateMinutes( end));
		assert( 60 > DailyCalendarRule::calculateSeconds( start));
		assert( 60 > DailyCalendarRule::calculateSeconds( end));
		dailyRanges_.merge( start, end);
	}

// ---------------------------------------------------------------------------------------------------------------------

	void DailyCalendarRule::mergeDailyPeriodsForOneDay( const Calendar::Date& day, Calendar::PeriodSet& periods)
	{
		DailyRanges::const_iterator 
			curI = dailyRanges_.begin(),
			endI = dailyRanges_.end();
		while( curI != endI)
		{
			periods.merge( 
				this->calculateTime( day, dailyRanges_.interval_start( *curI)),
				this->calculateTime( day, dailyRanges_.interval_end( *curI)));
			++curI;
		}
	}

// ---------------------------------------------------------------------------------------------------------------------

	Calendar::Time DailyCalendarRule::calculateTime( const Calendar::Date& day, ShortTime time)
	{
		return Calendar::Time( day, 
			boost::posix_time::hours( calculateHours( time))
			+ boost::posix_time::minutes( calculateMinutes( time))
			+ boost::posix_time::seconds( calculateSeconds( time)));
	}

// ---------------------------------------------------------------------------------------------------------------------

	unsigned int DailyCalendarRule::calculateHours( ShortTime time)
	{
		return time / 10000;
	}

// ---------------------------------------------------------------------------------------------------------------------

	unsigned int DailyCalendarRule::calculateMinutes( ShortTime time)
	{
		return (time / 100) % 100;
	}

// ---------------------------------------------------------------------------------------------------------------------

	unsigned int DailyCalendarRule::calculateSeconds( ShortTime time)
	{
		return time % 100;
	}

// ---------------------------------------------------------------------------------------------------------------------

	void DailyCalendarRule::getPeriods( const Calendar::Time& start, const Calendar::Time& end, Calendar::PeriodSet& out)
	{
		assert( out.empty());
		Calendar::PeriodSet periods;
		this->calculatePeriodSet( start, end, periods);
		out.swap( periods);
	}

} // namespace Model
